﻿/**
 * @file fwave_pcm.h
 * Copyright (c) Gaaagaa. All rights reserved.
 * 
 * @author  : Gaaagaa
 * @date    : 2020-02-16
 * @version : 1.0.0.0
 * @brief   : 实现 WAVE 文件读写 PCM 音频数据格式的工具类。
 */

#ifndef __FWAVE_PCM_H__
#define __FWAVE_PCM_H__

#include "xtypes.h"

////////////////////////////////////////////////////////////////////////////////

/**
 * @struct x_pcm_format_t
 * @brief  PCM 数据格式描述信息。
 */
typedef struct x_pcm_format_t
{
    x_uint32_t xut_samprate;  ///< 采样率，如 8000, 22050, 44100, 48000 等
    x_uint16_t xut_sampbits;  ///< 每个采样点的bit数，一般为 8, 16, 32 等
    x_uint16_t xut_channels;  ///< 通道数，单通道(Mono)值为1，双通道(Stereo)值为2，等等
} x_pcm_format_t;

typedef long x_fsize_t;

////////////////////////////////////////////////////////////////////////////////
// fwave_rpcm_t

/**
 * @class fwave_rpcm_t
 * @brief 对 WAVE 文件的 PCM 音频数据进行 读 操作的工具类。
 */
class fwave_rpcm_t
{
    // common data types
protected:
    typedef x_pcm_format_t x_format_t;

public:
    /**
     * @enum  xerrno_table_t
     * @brief 错误码表。
     */
    typedef enum xerrno_table_t
    {
        //======================================
        // open() 接口产生的错误码

        X_ERR_FILENAME_NULL  = 0x00000001,  ///< 文件名称为 X_NULL
        X_ERR_FOPEN_FAILED   = 0x00000002,  ///< 打开文件失败
        X_ERR_FREAD_RIFFTAG  = 0x00000003,  ///< 读取 RIFF 标签失败
        X_ERR_CMPID_RIFFTAG  = 0x00000004,  ///< 比较 RIFF 标签失败
        X_ERR_FREAD_WAVETAG  = 0x00000005,  ///< 读取 WAVE 标签失败
        X_ERR_CMPID_WAVETAG  = 0x00000006,  ///< 比较 WAVE 标签失败
        X_ERR_FREAD_WAVEND   = 0x00000007,  ///< 读取 WAVE 节点头失败
        X_ERR_FMT_REDUNDANT  = 0x00000008,  ///< 冗余的 fmt 节点
        X_ERR_FMT_CTXTSIZE   = 0x00000009,  ///< fmt 节点大小错误
        X_ERR_FMT_FREADNODE  = 0x0000000A,  ///< 读取 fmt 节点错误
        X_ERR_FMT_CONTEXT    = 0x0000000B,  ///< fmt 节点内容有误
        X_ERR_DATA_REDUNDANT = 0x0000000C,  ///< 冗余的 data 节点
        X_ERR_LACKOF_FMT     = 0x0000000D,  ///< 缺少 fmt 节点
        X_ERR_LACKOF_DATA    = 0x0000000E,  ///< 缺少 data 节点

        //======================================
    } xerrno_table_t;

    // common invoking
public:
    /**********************************************************/
    /**
     * @brief 获取 xerrno_table_t 中的错误码名称。
     */
    static x_cstring_t xerrno_name(x_int32_t xit_errno)
    {
        x_cstring_t xszt_name = "";

        switch (xit_errno)
        {
        //======================================
        // open() 接口产生的错误码

        case X_ERR_FILENAME_NULL  : xszt_name = "X_ERR_FILENAME_NULL" ; break;
        case X_ERR_FOPEN_FAILED   : xszt_name = "X_ERR_FOPEN_FAILED"  ; break;
        case X_ERR_FREAD_RIFFTAG  : xszt_name = "X_ERR_FREAD_RIFFTAG" ; break;
        case X_ERR_CMPID_RIFFTAG  : xszt_name = "X_ERR_CMPID_RIFFTAG" ; break;
        case X_ERR_FREAD_WAVETAG  : xszt_name = "X_ERR_FREAD_WAVETAG" ; break;
        case X_ERR_CMPID_WAVETAG  : xszt_name = "X_ERR_CMPID_WAVETAG" ; break;
        case X_ERR_FREAD_WAVEND   : xszt_name = "X_ERR_FREAD_WAVEND"  ; break;
        case X_ERR_FMT_REDUNDANT  : xszt_name = "X_ERR_FMT_REDUNDANT" ; break;
        case X_ERR_FMT_CTXTSIZE   : xszt_name = "X_ERR_FMT_CTXTSIZE"  ; break;
        case X_ERR_FMT_FREADNODE  : xszt_name = "X_ERR_FMT_FREADNODE" ; break;
        case X_ERR_FMT_CONTEXT    : xszt_name = "X_ERR_FMT_CONTEXT"   ; break;
        case X_ERR_DATA_REDUNDANT : xszt_name = "X_ERR_DATA_REDUNDANT"; break;
        case X_ERR_LACKOF_FMT     : xszt_name = "X_ERR_LACKOF_FMT"    ; break;
        case X_ERR_LACKOF_DATA    : xszt_name = "X_ERR_LACKOF_DATA"   ; break;

        //======================================
        default: break;
        }

        return xszt_name;
    }

    /**********************************************************/
    /**
     * @brief 获取 xerrno_table_t 中的错误码名称。
     */
    static x_cstring_t xerrno_name(x_errno_t xerr_no)
    {
        return xerrno_name(XERR_HINO(xerr_no));
    }

    // constructor/destructor
public:
    fwave_rpcm_t(void);
    ~fwave_rpcm_t(void);

    // public interfaces
public:
    /**********************************************************/
    /**
     * @brief 打开文件。
     * 
     * @param [in ] xszt_filename : 目标操作的文件路径名。
     * 
     * @return x_errno_t : 错误码。
     * @retval X_ERR_OK, 操作成功。
     * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
     * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
     */
    x_errno_t open(x_cstring_t xszt_filename);

    /**********************************************************/
    /**
     * @brief 关闭文件。
     */
    x_void_t close(void);

    /**********************************************************/
    /**
     * @brief 判断是否文件已经打开。
     */
    inline x_bool_t is_open(void) const
    {
        return (X_NULL != m_xht_file);
    }

    /**********************************************************/
    /**
     * @brief 当前操作的 PCM 数据格式信息。
     */
    inline const x_format_t & format(void) const
    {
        return m_fmt_info;
    }

    /**********************************************************/
    /**
     * @brief 返回文件中总的 PCM 数据字节数（等于 -1 时，表示无效）。
     */
    x_fsize_t size(void) const;

    /**********************************************************/
    /**
     * @brief 返回当前读取音频数据的偏移位置（等于 -1 时，表示无效）。
     */
    x_fsize_t offset(void) const;

    /**********************************************************/
    /**
     * @brief 设置当前读取音频数据的偏移位置。
     * 
     * @param [in ] xst_apos : 偏移位置（为 -1 时，则偏移到文件末尾）。
     * 
     * @return x_int32_t
     *         - 返回  0，表示操作成功；
     *         - 返回 -1，表示操作失败。
     */
    x_int32_t seek(x_fsize_t xst_apos);

    /**********************************************************/
    /**
     * @brief 读取 PCM 数据。
     * 
     * @param [out] xct_dptr : 读取 PCM 数据所使用的缓存。
     * @param [in ] xst_size : 所使用缓存的大小。
     * 
     * @return x_fsize_t
     *         - 成功，返回 读取到的字节数（>= 0）。
     */
    x_fsize_t read(x_void_t * xct_dptr, x_fsize_t xst_size);

    /**********************************************************/
    /**
     * @brief 判断当前是否已经读到音频数据末尾。
     */
    x_bool_t is_eof(void) const;

    // data members
protected:
    x_handle_t  m_xht_file;  ///< 文件句柄
    x_format_t  m_fmt_info;  ///< PCM 数据格式描述信息
    x_fsize_t   m_xst_abeg;  ///< 音频数据的起始偏移位置
    x_fsize_t   m_xst_alen;  ///< 音频数据的总字节数
};

////////////////////////////////////////////////////////////////////////////////
// fwave_wpcm_t

/**
 * @class fwave_rpcm_t
 * @brief 将 PCM 音频数据 写入 WAVE 文件的工具类。
 */
class fwave_wpcm_t
{
    // common data types
protected:
    typedef x_pcm_format_t x_format_t;

public:
    /**
     * @enum  xerrno_table_t
     * @brief 错误码表。
     */
    typedef enum xerrno_table_t
    {
        //======================================
        // open() 接口产生的错误码

        X_ERR_BAD_ARGS       = 0x00000001,  ///< 参数错误
        X_ERR_FOPEN_FAILED   = 0x00000002,  ///< 打开文件失败
        X_ERR_FWRITE_INFO    = 0x00000003,  ///< 写入 WAVE 文件信息失败

        //======================================
    } xerrno_table_t;

    // common invoking
public:
    /**********************************************************/
    /**
     * @brief 获取 xerrno_table_t 中的错误码名称。
     */
    static x_cstring_t xerrno_name(x_int32_t xit_errno)
    {
        x_cstring_t xszt_name = "";

        switch (xit_errno)
        {
        //======================================
        // open() 接口产生的错误码

        case X_ERR_BAD_ARGS     : xszt_name = "X_ERR_BAD_ARGS"    ; break;
        case X_ERR_FOPEN_FAILED : xszt_name = "X_ERR_FOPEN_FAILED"; break;
        case X_ERR_FWRITE_INFO  : xszt_name = "X_ERR_FWRITE_INFO" ; break;

        //======================================
        default: break;
        }

        return xszt_name;
    }

    /**********************************************************/
    /**
     * @brief 获取 xerrno_table_t 中的错误码名称。
     */
    static x_cstring_t xerrno_name(x_errno_t xerr_no)
    {
        return xerrno_name(XERR_HINO(xerr_no));
    }

    // constructor/destructor
public:
    fwave_wpcm_t(void);
    ~fwave_wpcm_t(void);

    // public interfaces
public:
    /**********************************************************/
    /**
     * @brief 打开文件。
     * 
     * @param [in ] xszt_filename : 目标操作的文件路径名。
     * @param [in ] xut_samprate  : 采样率。
     * @param [in ] xut_sampbits  : 每个采样点的位数。
     * @param [in ] xut_channels  : 通道数。
     * 
     * @return x_errno_t : 错误码。
     * @retval X_ERR_OK, 操作成功。
     * @retval XERR_HINO(xerr_no)，参看 xerrno_table_t 相关枚举值。
     * @retval XERR_LONO(xerr_no)，所引用的模块返回的错误码值。
     */
    x_errno_t open(x_cstring_t xszt_filename,
                   x_uint32_t xut_samprate,
                   x_uint16_t xut_sampbits,
                   x_uint16_t xut_channels);

    /**********************************************************/
    /**
     * @brief 关闭文件。
     */
    x_void_t close(void);

    /**********************************************************/
    /**
     * @brief 判断是否文件已经打开。
     */
    inline x_bool_t is_open(void) const
    {
        return (X_NULL != m_xht_file);
    }

    /**********************************************************/
    /**
     * @brief 当前操作的 PCM 数据格式信息。
     */
    inline const x_format_t & format(void) const
    {
        return m_fmt_info;
    }

    /**********************************************************/
    /**
     * @brief 返回当前文件中总的 PCM 数据字节数（等于 -1 时，表示无效）。
     */
    x_fsize_t size(void) const;

    /**********************************************************/
    /**
     * @brief 返回当前写入音频数据的偏移位置（等于 -1 时，表示无效）。
     */
    x_fsize_t offset(void) const;

    /**********************************************************/
    /**
     * @brief 设置当前写入音频数据的偏移位置。
     * 
     * @param [in ] xst_apos : 偏移位置（为 -1 时，则偏移到文件末尾）。
     * 
     * @return x_int32_t
     *         - 返回  0，表示操作成功；
     *         - 返回 -1，表示操作失败。
     */
    x_int32_t seek(x_fsize_t xst_apos);

    /**********************************************************/
    /**
     * @brief 写入 PCM 数据。
     * 
     * @param [in ] xct_dptr : PCM 数据缓存。
     * @param [in ] xst_size : 缓存中有效数据的大小。
     * 
     * @return x_fsize_t
     *         - 成功，返回 写入到的字节数（>= 0）；
     *         - 失败，返回 -1。
     */
    x_fsize_t write(x_void_t * xct_dptr, x_fsize_t xst_size);

    // data members
private:
    x_handle_t  m_xht_file;  ///< 文件句柄
    x_format_t  m_fmt_info;  ///< PCM 数据格式描述信息
};

////////////////////////////////////////////////////////////////////////////////

#endif // __FWAVE_PCM_H__
